package cn.wizzer.app.web.modules.controllers.platform.bus;

import cn.wizzer.app.bus.modules.models.BusinessFileInfo;
import cn.wizzer.app.bus.modules.models.FileInfo;
import cn.wizzer.app.bus.modules.models.YJbxx;
import cn.wizzer.app.bus.modules.models.YJbxxLcxx;
import cn.wizzer.app.bus.modules.services.BusinessFileInfoService;
import cn.wizzer.app.bus.modules.services.YJbxxLcxxService;
import cn.wizzer.app.bus.modules.services.YJbxxService;
import cn.wizzer.app.web.commons.base.Globals;
import cn.wizzer.app.web.commons.doc.BookMark;
import cn.wizzer.app.web.commons.doc.BookMarks;
import cn.wizzer.app.web.commons.doc.DocUtils;
import cn.wizzer.app.web.commons.slog.annotation.SLog;
import cn.wizzer.app.web.commons.utils.DateUtil;
import cn.wizzer.app.web.commons.utils.StringUtil;
import cn.wizzer.app.web.modules.controllers.open.file.UploadController;
import cn.wizzer.framework.base.Result;
import com.fasterxml.jackson.databind.ObjectMapper;
import net.sf.json.JSONObject;
import org.apache.commons.net.util.Base64;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.extractor.WordExtractor;
import org.apache.poi.ooxml.POIXMLDocument;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Strings;
import org.nutz.lang.Times;
import org.nutz.lang.util.NutMap;
import org.nutz.log.Log;
import org.nutz.log.Logs;
import org.nutz.mvc.annotation.*;
import org.nutz.mvc.upload.TempFile;
import org.nutz.mvc.upload.UploadAdaptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.channels.FileLock;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 在线office
 */
@IocBean
@At("/platform/wopi")
public class WopiHostContrller {

    private static final Log log = Logs.get();

    @Inject
    private BusinessFileInfoService businessFileInfoService;
    @Inject
    private YJbxxService yjbxxService;
    @Inject
    private YJbxxLcxxService yjbxxlcxxservice;

    @At("/files/?/contents")
    @GET
    @Ok("raw")
    @SLog(tag = "下载文件", msg = "样品id:${args[0]}")
    public void getFile(String data, HttpServletResponse response) {
        try {
            String fileName = data;
            BusinessFileInfo report = businessFileInfoService.fetch(Cnd.where("delFlag", "=", "0").and("fileName", "=", fileName.substring(0, fileName.lastIndexOf("."))));
            String fileNameDocx = fileName;
            String date = DateUtil.format(new Date(), "yyyy") + "/" + DateUtil.format(new Date(), "MM") + "/" + DateUtil.format(new Date(), "dd");
            String filePath = "/report/" + date + "/" + fileNameDocx;
            String uploadPath = this.getUploadPathConfig();
            if (report != null) {
                filePath = report.getFilePath();
            }else{
                report = new BusinessFileInfo();
                report.setInfoName(fileName);
                report.setInfoVersion(date);
                report.setInfoType("report");
                report.setRemark("");
                report.setOperatorName(StringUtil.getPlatformUsername());
                report.setFileType(".docx");
                report.setDisabled(false);
                report.setFilePath(filePath);
                report.setFileName(fileName.substring(0, fileName.lastIndexOf(".")));
                report.setFileBeforeName(fileNameDocx);
                report.setFileUrl(uploadPath + filePath);
                businessFileInfoService.insert(report);
            }
            String path = uploadPath + filePath;
            File file = new File(path);
            if (!file.exists()&&data.length()>1) {
                String id = fileName.substring(0,fileName.indexOf("fy."));
                YJbxx obj = yjbxxService.fetchLinks(yjbxxService.fetch(id),"yjbxxjyxm");
                /*Map<String, String> map = new HashMap<String, String>();
                map.put("P3_ypbh", obj.getYpbh());*/
                BusinessFileInfo businessFileInfo = businessFileInfoService.fetch(Cnd.where("delFlag", "=", "0").and("infoName", "=", "附页").and("productType","like","%"+obj.getYplx()+"%").and("disabled", "=", false));
                if (businessFileInfo != null) {
                    String reportTemplateFile = businessFileInfo.getFilePath();
                    List<String> count = obj.getYjbxxjyxm().stream().filter(s -> s.getInspectionItemOrder()!=null&&s.getInspectionItemOrder()>0).map(t->t.getInspectionItemOrder().toString()).distinct().collect(Collectors.toList());
                    DocUtils.FileToFileFy(uploadPath + reportTemplateFile,uploadPath + filePath,null,count);
                } else {
                    file.createNewFile();
                }
            }

            String filename = file.getName();
            InputStream fis = new BufferedInputStream(new FileInputStream(path));
            OutputStream toClient = new BufferedOutputStream((OutputStream) response.getOutputStream());
            byte[] buffer = new byte[fis.available()];
            fis.read(buffer);
            response.reset();
            response.addHeader("Content-Disposition", "attachment;filename=" + new String(filename
                    .getBytes("UTF-8"), "ISO-8859-1"));
            response.addHeader("Content-Length", String.valueOf(file.length()));
            response.setContentType("application/octet-stream");
            toClient.write(buffer);
            toClient.flush();
            toClient.close();
            fis.close();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            e.printStackTrace();
        }
    }



    @At("/files/?/contents")
    @POST
    @Ok("raw")
    @SLog(tag = "保存文件", msg = "样品id:${args[0]}")
    public void postFile(String fileName, InputStream ins,HttpServletRequest request,@Param("access_token") String access_token) {

        try {
            BusinessFileInfo report = businessFileInfoService.fetch(Cnd.where("delFlag", "=", "0").and("fileName", "=", fileName.substring(0, fileName.lastIndexOf("."))));
            if (report != null) {
                String filePath = report.getFilePath();
                String uploadPath = this.getUploadPathConfig();
                String path = uploadPath + filePath;

                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                OutputStream outs = new FileOutputStream(path);
                XWPFDocument document = new XWPFDocument(ins);
                document.write(baos);
                document.close();
                outs.write(baos.toByteArray());
                baos.close();
                outs.close();
                //System.out.println(file.lastModified());

                /*File file = new File(path);
                FileOutputStream fop = new FileOutputStream(file);
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                byte[] buffer = new byte[1024];
                int num = ins.read(buffer);
                while (num != -1) {
                    baos.write(buffer, 0, num);
                    num = ins.read(buffer);
                }
                baos.flush();
                baos.toByteArray();
                fop.write(baos.toByteArray());
                baos.close();
                fop.flush();
                fop.close();*/
                ins.close();
               /* System.out.println("保存完成啦");*/
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            e.printStackTrace();
        }
    }

    @At("/files/?")
    @GET
    @Ok("raw")
    @SLog(tag = "打开文件", msg = "样品id:${args[0]}")
    public void getFileInfo(String fileName, HttpServletRequest request, HttpServletResponse response) {
        FileInfo info = new FileInfo();
        try {
            String fileNameDocx = fileName + ".docx";
            String date = DateUtil.format(new Date(), "yyyy") + "/" + DateUtil.format(new Date(), "MM") + "/" + DateUtil.format(new Date(), "dd");
            String filePath = "/report/" + date + "/" + fileNameDocx;
            String uploadPath = this.getUploadPathConfig();
            BusinessFileInfo report = businessFileInfoService.fetch(Cnd.where("delFlag", "=", "0").and("fileName", "=", fileName));
            if (report != null) {
                filePath = report.getFilePath();
            }else{
                report = new BusinessFileInfo();
                report.setInfoName(fileName);
                report.setInfoVersion(date);
                report.setInfoType("report");
                report.setRemark("");
                report.setOperatorName(StringUtil.getPlatformUsername());
                report.setInfoType(".docx");
                report.setDisabled(false);
                report.setFilePath(filePath);
                report.setFileName(fileName);
                report.setFileBeforeName(fileNameDocx);
                report.setFileUrl(uploadPath + filePath);
                businessFileInfoService.insert(report);
            }
            File file = new File(uploadPath + filePath);
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }
            /*int a = 0;
            while (!file.exists()){
                Thread.sleep(1000); //编制附页的时候，附页有可能没来得及创建，如何没有创建先等待1秒。
                a++;
                if (a>10){ //10秒后不管有没有都跳出
                    break;
                }
            }*/
            if(!file.exists()){
                //编制附页的时候如果没有附页文件则创建
                //先获取样品信息
                String id = fileName.replace("fy","");
                YJbxx obj = yjbxxService.fetchLinks(yjbxxService.fetch(id),"yjbxxjyxm");
                /*Map<String, String> map = new HashMap<String, String>();
                map.put("P3_ypbh", obj.getYpbh());*/
                BusinessFileInfo businessFileInfo = businessFileInfoService.fetch(Cnd.where("delFlag", "=", "0").and("infoName", "=", "附页").and("productType","like","%"+obj.getYplx()+"%").and("disabled", "=", false));
                if (businessFileInfo != null) {
                    String reportTemplateFile = businessFileInfo.getFilePath();
                    List<String> count = obj.getYjbxxjyxm().stream().filter(s -> s.getInspectionItemOrder()!=null&&s.getInspectionItemOrder()>0).map(t->t.getInspectionItemOrder().toString()).distinct().collect(Collectors.toList());
                    DocUtils.FileToFileFy(uploadPath + reportTemplateFile,uploadPath + filePath,null,count);
                } else {
                    file.createNewFile();
                }
            }
            info.setBaseFileName(file.getName());
            info.setSize(file.length());
            info.setOwnerId("admin");
            info.setVersion(file.lastModified());
            info.setSha256(getHash256(file));
            info.setAllowExternalMarketplace(true);
            info.setUserCanWrite(true);
            info.setSupportsUpdate(true);
            info.setSupportsLocks(true);
            ObjectMapper mapper = new ObjectMapper();
            String str = mapper.writeValueAsString(info);
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write(str);

        } catch (Exception e) {
            log.error(e.getMessage(), e);
            e.printStackTrace();
        }
    }



    public static boolean isFileLocked(File file) {
        boolean locked = false;
        try (RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
             FileLock fileLock = randomAccessFile.getChannel().tryLock()) {
            locked = fileLock == null;
        } catch (Exception e) {
            // 文件正在被使用
            locked = true;
        }
        return locked;
    }
    @AdaptBy(type = UploadAdaptor.class, args = {"ioc:fileUpload"})
    @Aop(TransAop.READ_COMMITTED)
    @At("/upAttached")
    @POST
    @Ok("json")
    @RequiresAuthentication
    @SLog(tag = "上传附页文件", msg = "样品id:${args[0]}")
    public Object upAttached(@Param("Filedata") TempFile tf, @Param("fileName")String fileName, HttpServletRequest request, HttpServletResponse response) {
        try {
            fileName = fileName + "fy";
            String suffixName = tf.getSubmittedFileName().substring(tf.getSubmittedFileName().lastIndexOf(".")).toLowerCase();
            String fileNameDocx = fileName + ".docx";
            //文件后缀名

            String date = DateUtil.format(new Date(), "yyyy") + "/" + DateUtil.format(new Date(), "MM") + "/" + DateUtil.format(new Date(), "dd");
            String filePath = "/report/" + date + "/" + fileNameDocx;
            BusinessFileInfo report = businessFileInfoService.fetch(Cnd.where("delFlag", "=", "0").and("fileName", "=", fileName));
            if (report != null) {
                filePath = report.getFilePath();
                businessFileInfoService.delete(report.getId());
            }
            String uploadPath = this.getUploadPathConfig();
            File file = new File(uploadPath + filePath);
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }
            if(suffixName.equals(".doc")) {
                return Result.error("上传失败,请上传docx 文件");
            }
            if (file.exists()) {
                if(isFileLocked(file)){
                    return Result.error("文件正在被其它程序占用，请稍后上传！");
                }
                file.delete();
            }
            tf.write(uploadPath + filePath);
            //businessFileInfoService.execute(Sqls.create("delete from bus_file_info  where fileName = '"+fileName+"'"));

            report = new BusinessFileInfo();
            report.setInfoName(fileName);
            report.setInfoVersion(date);
            report.setInfoType("report");
            report.setRemark("");
            report.setOperatorName(StringUtil.getPlatformUsername());
            report.setFileType(".docx");
            report.setDisabled(false);
            report.setFilePath(filePath);
            report.setFileName(fileName);
            report.setFileBeforeName(fileNameDocx);
            report.setFileUrl(uploadPath + filePath);
            businessFileInfoService.insert(report);
            YJbxx obj = yjbxxService.fetch(fileName.substring(0,fileName.length()-2));
            obj.setLczt("4");
            YJbxxLcxx lcxx = yjbxxlcxxservice.fetch(Cnd.NEW().and("jbxxId", "=", obj.getId()).and("delFlag","=","0").and("lczt", "=", "2"));
            lcxx.setJbxxId(obj.getId());
            lcxx.setJyry("");
            lcxx.setLczt(obj.getLczt());
            lcxx.setSpyj("编制中，上传附页");
            lcxx.setOpBy(StringUtil.getPlatformUid());
            lcxx.setOpAt(Times.getTS());
            yjbxxlcxxservice.execute(Sqls.create("update y_jbxx_lcxx set delflag=1 where lczt>="+obj.getLczt()+" and jbxxid='"+obj.getId()+"'"));
            yjbxxlcxxservice.insert(lcxx);
            yjbxxService.update(obj);
            NutMap map = NutMap.NEW()
                    .addv("file_type", report.getInfoType())//返回后缀名
                    .addv("file_name", fileName)//返回生成的随机文件名
                    .addv("file_size", tf.getSize())//返回文件大小
                    .addv("file_path", filePath)//返回相对路径（相对配置的上传路径）
                    .addv("file_url", uploadPath + filePath);//返回绝对路径
            Result res = Result.success("上传成功").addData(map);
            return res;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            e.printStackTrace();
            return Result.error("上传失败");
        }
    }

    /**
     * 复制附页
     *
     * @param
     * @return
     */
    @Aop(TransAop.READ_COMMITTED)
    @At("/doCopyAttached/?/?")
    @Ok("json")
    @RequiresAuthentication
    @SLog(tag = "复制附页", msg = "复制附页:${args[0]}到${args[1]}")
    public Object doCopyAttached(String fileName, String newFileName) {
        try {
            //新附页路径请在前端拼接（可区别于用户上传的目录）,下面会在此路径前拼接系统文件的上传路径
            String uploadPath = this.getUploadPathConfig();
            String date = DateUtil.format(new Date(), "yyyy") + "/" + DateUtil.format(new Date(), "MM") + "/" + DateUtil.format(new Date(), "dd");
            String filePath = "";
            String newFilePath = "/report/" + date + "/" + newFileName + ".docx";

            BusinessFileInfo newReport = businessFileInfoService.fetch(Cnd.where("delFlag", "=", "0").and("fileName", "=", newFileName));
            if (newReport != null) {
                return Result.error("当前报告附页文件已存在，无法复制，请直接编辑或删除！");
            }
            BusinessFileInfo report = businessFileInfoService.fetch(Cnd.where("delFlag", "=", "0").and("fileName", "=", fileName));
            if (report != null) {
                filePath = report.getFilePath();
            } else {
                return Result.error("选中文档附页文件不存在，无法复制！");
            }

            File oldFile = new File(uploadPath + filePath);
            File newFile = new File(uploadPath + newFilePath);
            if (!oldFile.exists()) {
                return Result.error(oldFile.getName() + "附页文件不存在，无法复制！");
            } else {
                if (!newFile.getParentFile().exists()) {
                    newFile.getParentFile().mkdirs();
                }
                FileInputStream in = new FileInputStream(oldFile);
                FileOutputStream out = new FileOutputStream(newFile);
                byte[] buf = new byte[1024];
                int bytesRead;
                while ((bytesRead = in.read(buf)) != -1) {
                    out.write(buf, 0, bytesRead);
                }
                in.close();
                out.close();
                /*System.out.println("复制了一个文件" + newFile.getName());*/
                newReport = new BusinessFileInfo();
                newReport.setInfoName(newFileName);
                newReport.setInfoVersion(date);
                newReport.setInfoType("report");
                newReport.setRemark("");
                newReport.setOperatorName(StringUtil.getPlatformUsername());
                report.setInfoType(".docx");
                newReport.setDisabled(false);
                newReport.setFilePath(newFilePath);
                newReport.setFileName(newFileName);
                newReport.setFileBeforeName(newFileName + ".docx");
                newReport.setFileUrl(uploadPath + newFilePath);
                businessFileInfoService.insert(newReport);

                YJbxx obj = yjbxxService.fetch(newFileName.substring(0,newFileName.length()-2));
                obj.setLczt("4");
                YJbxxLcxx lcxx = yjbxxlcxxservice.fetch(Cnd.NEW().and("jbxxId", "=", obj.getId()).and("delFlag","=","0").and("lczt", "=", "2"));
                lcxx.setJbxxId(obj.getId());
                lcxx.setJyry("");
                lcxx.setLczt(obj.getLczt());
                lcxx.setSpyj("编制中，复制附页");
                lcxx.setOpBy(StringUtil.getPlatformUid());
                lcxx.setOpAt(Times.getTS());
                yjbxxlcxxservice.execute(Sqls.create("update y_jbxx_lcxx set delflag=1 where lczt>="+obj.getLczt()+" and jbxxid='"+obj.getId()+"'"));
                yjbxxlcxxservice.insert(lcxx);
                yjbxxService.update(obj);
            }
            return Result.success("复制成功！");
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            e.printStackTrace();
            return Result.error("system.error");
        }
    }

    /**
     * 删除附页
     *
     * @param
     * @return
     */
    @Aop(TransAop.READ_COMMITTED)
    @At("/delAttached/?")
    @Ok("json")
    @RequiresAuthentication
    @SLog(tag = "删除附页", msg = "删除附页:${args[0]}")
    public Object delAttached(String fileName) {
        try {
            BusinessFileInfo report = businessFileInfoService.fetch(Cnd.where("delFlag", "=", "0").and("fileName", "=", fileName));
            if (report != null) {
                String uploadPath = this.getUploadPathConfig();
                File file = new File(uploadPath + report.getFilePath());
                if (file.exists()) {
                    if (file.isFile()) {//boolean isFile():测试此抽象路径名表示的文件是否是一个标准文件。
                        file.delete();
                    }
                }

                businessFileInfoService.delete(report.getId());


                YJbxx obj = yjbxxService.fetch(fileName.substring(0,fileName.length()-2));
                obj.setLczt("4");
                YJbxxLcxx lcxx = yjbxxlcxxservice.fetch(Cnd.NEW().and("jbxxId", "=", obj.getId()).and("delFlag","=","0").and("lczt", "=", "2"));
                lcxx.setJbxxId(obj.getId());
                lcxx.setLczt(obj.getLczt());
                lcxx.setJyry("");
                lcxx.setSpyj("编制中，删除附页");
                lcxx.setOpBy(StringUtil.getPlatformUid());
                lcxx.setOpAt(Times.getTS());
                yjbxxlcxxservice.execute(Sqls.create("update y_jbxx_lcxx set delflag=1 where lczt>="+obj.getLczt()+" and jbxxid='"+obj.getId()+"'"));
                yjbxxlcxxservice.insert(lcxx);
                yjbxxService.update(obj);
                return Result.success("删除成功！");
            }
            return Result.error("文件不存在！");
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            e.printStackTrace();
            return Result.error("system.error");
        }
    }

    /**
     * 获取文件的SHA-256值
     *
     * @param file
     * @return
     */
    private String getHash256(File file) throws IOException, NoSuchAlgorithmException {
        String value = "";
        try (InputStream fis = new FileInputStream(file)) {
            int numRead;
            byte[] buffer = new byte[1024];


            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            do {
                numRead = fis.read(buffer);
                if (numRead <= 0)
                    continue;
                digest.update(buffer, 0, numRead);
            }
            while (numRead != -1);

            value = new String(Base64.encodeBase64(digest.digest()));
        }
        return value;
    }

    private String getUploadPathConfig() throws Exception {
        String uploadPath;
        //系统没开启配置,上传到项目路径
        if ("false".equals(Globals.MyConfig.getOrDefault("IsConfigUploadPath", "false"))) {
            uploadPath = UploadController.class.getClassLoader().getResource("").getPath() + "upload";
        } else if (
                ("true".equals(Globals.MyConfig.getOrDefault("IsConfigUploadPath", "false"))) && (Globals.MyConfig.getOrDefault("ConfigUploadPath", null) != null)
        ) {
            uploadPath = Globals.MyConfig.get("ConfigUploadPath").toString();
        } else {
            throw new Exception("文件上传路径配置有误");
        }
        return uploadPath;
    }
}

